package io.gitee.lglbc.easy.security.core.security;

import io.gitee.lglbc.easy.security.core.config.EasySecurityProperties;
import io.gitee.lglbc.easy.security.core.token.EasyPayload;
import io.gitee.lglbc.easy.security.core.token.TokenService;
import io.gitee.lglbc.easy.security.open.DefaultSecurityResultHandler;
import io.gitee.lglbc.easy.security.open.EasySecurityResultHandler;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 乐哥聊编程
 */
@Configuration
@EnableMethodSecurity
@EnableConfigurationProperties(EasySecurityProperties.class)
public class EasySecurityConfig {
    @Autowired
    private EasySecurityProperties easySecurityProperties;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private EasySecurityResultHandler easySecurityResultHandler;
    @Autowired
    private TokenService tokenService;
    private List<RequestMatcher> requestMatchers;

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers((request -> {
                    for (RequestMatcher requestMatcher : requestMatchers) {
                        if (requestMatcher.matches(request)) {
                            return true;
                        }
                    }
                    return false;
                })
        );
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authorize) -> authorize.requestMatchers("/security/captcha").permitAll()
                        .anyRequest().authenticated()).formLogin((form) -> form.permitAll())
                .addFilterBefore(easyAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(easyLoginFilter(), EasyLoginFilter.class)
                .exceptionHandling((handle) -> {
                    handle.accessDeniedHandler((request, response, accessDeniedException) -> {
                        easySecurityResultHandler.noPermissionHandler(request, response, accessDeniedException);
                    });
                    handle.authenticationEntryPoint((request, response, authenticationException) -> {
                        easySecurityResultHandler.noPermissionHandler(request, response, authenticationException);
                    });
                })
                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .csrf((csrf) -> csrf.disable());
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager() {

        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(provider);
    }

    @Bean
    public EasyLoginFilter easyLoginFilter() {
        EasyLoginFilter easyLoginFilter = new EasyLoginFilter();
        easyLoginFilter.setAuthenticationManager(authenticationManager());
        easyLoginFilter.setAuthenticationSuccessHandler(getSuccessHandler());
        easyLoginFilter.setAuthenticationFailureHandler(getFailureHandler());
        return easyLoginFilter;
    }

    @Bean
    public EasyAuthenticationFilter easyAuthenticationFilter(){
        return new EasyAuthenticationFilter();
    }
    protected AuthenticationFailureHandler getFailureHandler() {
        return (request, response, exception) -> {
            easySecurityResultHandler.loginFailedHandler(request, response, exception);
        };
    }

    private AuthenticationSuccessHandler getSuccessHandler() {
        return ((request, response, authentication) -> {
            EasyPayload payloadDto = initPayload(authentication.getName(), authentication.getAuthorities());
            String token = tokenService.generateToken(payloadDto);
            easySecurityResultHandler.loginSuccessHandler(request, response, authentication, token);
        });
    }


    private EasyPayload initPayload(String user, Collection<? extends GrantedAuthority> authorities) {

        EasyPayload payloadDto = new EasyPayload();
        payloadDto.setUsername(user);
        if (!CollectionUtils.isEmpty(authorities)) {
            payloadDto.setAuthorities(authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
        }
        return payloadDto;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @PostConstruct
    public void init() {
        List<String> ignoreUrls = easySecurityProperties.getIgnoreUrls();
        if (CollectionUtils.isEmpty(ignoreUrls)) {
            ignoreUrls = new ArrayList<>();
        }
        ignoreUrls.add("/security/captcha");
        requestMatchers = new ArrayList<>();
        for (String pattern : ignoreUrls) {
            requestMatchers.add(new AntPathRequestMatcher(pattern, null));
        }

        if (easySecurityResultHandler == null) {
            easySecurityResultHandler = new DefaultSecurityResultHandler();
        }
    }
}
